package sicnu.cs.ich.ich.ranking.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import sicnu.cs.ich.api.common.Constants;
import sicnu.cs.ich.api.common.enums.ConfigEnum;
import sicnu.cs.ich.api.domain.dto.IchHitDTO;
import sicnu.cs.ich.api.domain.dto.IchHotDTO;
import sicnu.cs.ich.api.domain.mq.IchMQ;
import sicnu.cs.ich.common.common.concurrent.FrequencyCountQueue;
import sicnu.cs.ich.common.common.concurrent.HitCalculateQueue;
import sicnu.cs.ich.common.services.IConfigService;
import sicnu.cs.ich.common.services.impl.RedisService;
import sicnu.cs.ich.ich.ranking.mapper.IchMapper;
import sicnu.cs.ich.ich.ranking.service.IIchRankingService;

import java.text.DateFormat;
import java.util.*;


/**
 * @author Caike
 */
@Service
@Slf4j
//@RequiredArgsConstructor
@CacheConfig(cacheNames = "ich")
public class IchRankingServiceImpl implements IIchRankingService {

    @Autowired
    private IchMapper ichMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IConfigService configService;

    /**
     * 记录非遗热度排行榜
     */
    private static final HitCalculateQueue<IchHitDTO> ICH_RANKING = new HitCalculateQueue<>();
    /**
     * 记录非遗类别访问频度
     */
    private static final FrequencyCountQueue TYPE_RECORD = new FrequencyCountQueue(1000, 20);


    /**
     * 指定非遗热度+1
     */
    @Override
    public void ichHitIncrement(Integer ichId) {
        ICH_RANKING.increment(ichId);
    }

    /**
     * 指定非遗排行榜+1
     */
    @Override
    @Async
    public void ichProvinceIncrement(IchMQ ich) {
        log.info("非遗热度榜+1");
        redisService.zsetIncrease(Constants.REDIS_RANK_ICH_PROVINCE + ich.getProvince(), new IchHotDTO(ich.getIchId(), ich.getName(), 0), 1);
    }

    @Override
    public void ichHitIncrement(List<Integer> ids) {
        ICH_RANKING.increment(ids);
    }

    @Override
    public void ichTypeHitIncrement(Integer typeId) {
        try {
            TYPE_RECORD.update(typeId);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    @Deprecated
    public List<Integer> getIchRankingIds() throws InterruptedException {
//        if (redisService.containsKey(REDIS_ICH_RANK)) {
//            return JSON.parseArray((String) redisService.getValue(REDIS_ICH_RANK)).toJavaList(Integer.class);
//        }
        List<Integer> ids = ICH_RANKING.getRankingIds();
//        if (ICH_RANKING.isFull()) {
//            // 仅在HIT_RANKING存满时做缓存
//            log.info("ich_hit_rank is full:{}", ids.toString());
//            redisService.cacheValue(REDIS_ICH_RANK, JSON.toJSONString(ids), 59 * 60);
//            return ids;
//        }
        // TODO：暂时填充随机数据（现在启动，Ranking肯定是空啦）
        Random r = new Random();
        while (ids.size() < ICH_RANKING.getCapacity()) {
            int id = r.nextInt(3000);
            if (!ids.contains(id)) {
                ids.add(id);
            }
        }
        return ids;
    }

    @Override
    public int[] getIchTypeHit() {
        return TYPE_RECORD.getRecord();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class)
    public PriorityQueue<IchHitDTO> sortAndGet() {
        ICH_RANKING.sortRanking(IchHitDTO::new, this::getGapHour, this::updateGapHour);
        return ICH_RANKING.getRankingObjs();
    }
    /**
     * 获取热力更新榜时距离上次更新的时间间隔
     * @return
     */
    public Integer getGapHour(Void unused) {
        final String lastUpdated = configService.loadConfig(ConfigEnum.ICH_HIT_UPDATED);
        final long now = System.currentTimeMillis();
        return Integer.parseInt(String.valueOf(now - Double.parseDouble(lastUpdated))) / 1000 / 60 / 60;
    }
    /**
     * 更新热力更新榜时距离上次更新的时间间隔
     * <br/> 必须让该方法与更新排行榜处于同一个事务
     * @return
     */
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = RuntimeException.class)
    public void updateGapHour(Void unused){
        configService.storeConfig(ConfigEnum.ICH_HIT_UPDATED, String.valueOf(System.currentTimeMillis()));
    }


    @Override
    public void loadHitConfig(HashMap<ConfigEnum, String> configMap) {
        try {
            ICH_RANKING.loadConfig(configMap);
            TYPE_RECORD.loadConfig(configMap);
        } catch (InterruptedException e) {
            log.error("ICH_HIT 配置文件加载失败。");
            e.printStackTrace();
        }
        log.info("完成加载 ICH_HIT 配置文件");
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class)
    public HashMap<ConfigEnum, String> exportHitConfig() {
        log.info("导出 ICH_HIT 配置中");
        HashMap<ConfigEnum, String> config = new HashMap<>();
        ICH_RANKING.exportConfig(config);
        TYPE_RECORD.exportConfig(config);
        log.info("成功生成 ICH_HIT 配置");
        return config;
    }

    /**
     * 批量更新非遗热度
     *
     * @param ichRankRecordList
     */
    @Override
    public void batchIncrementIchHot(List<IchHotDTO> ichRankRecordList) {
        ichMapper.batchUpdateIchHot(ichRankRecordList);
    }

    /**
     * 获取前多少位排行榜
     *
     * @param num 前多少
     */
    @Override
    public List<IchHotDTO> getProvinceIchHotDTOs(String province, int num) {
        // 注意，这里从数据
        List<IchHotDTO> list = ichMapper.selectProvinceIchHotDTO(province, num);
        if (CollectionUtils.isEmpty(list)) {
            throw new NoSuchElementException("无数据");
        }
        return list;
    }

    private final static Integer UPDATE_HOT_VALUE = 5;

    /**
     * 助力非遗
     */
    @Override
    public void updateIchHit(Integer ichId) {
        // 一次助力，加5个影响分
        ICH_RANKING.incrementBy(ichId, 5);
        ichMapper.updateIchHot(ichId, UPDATE_HOT_VALUE);
    }

    @Override
    @CacheEvict(value = "ich::like::id", allEntries = true)
    public void clearLikeNum() {
        log.info("删除Redis点赞信息");
    }

}